﻿using UnityEngine;
using UnityEditor;
using System.Linq;
using System.Collections.Generic;

//------------------------------------------------------------
// shaco Framework
// Copyright © 2017-2025 chang.liu All rights reserved.
// Feedback: 449612236@qq.com
//------------------------------------------------------------

namespace shacoEditor
{
    /// <summary>
    /// 编辑器图集设置接口
    /// </summary>
    public class SpriteAtlasCollectDepencies
    {
        [MenuItem("Assets/shaco/SpriteAtlas/CollectDepencies", false)]
        static void CollectDepenciesInPrefab_Asset()
        {//UnityEngine.UI.Text
         //TextMeshProUGUI
         //LilitaOne-Regular Outline 72 SDF
            var atlasSettings = SpriteAtlasSettingsWindow.GetLastSelectAtlasSettings();
            var depenciesTexture = new Queue<KeyValuePair<string, Texture>>();
            foreach (var iter in Selection.gameObjects)
            {
                var depenciesPath = AssetDatabase.GetDependencies(AssetDatabase.GetAssetPath(iter), true);
                foreach (var path in depenciesPath)
                {
                    if (path.EndsWith(".asset") || path.Contains("/Editor") || path.Contains(SpriteAtlasSettingsWindow.IN_ATLAS_FOLDER))
                        continue;

                    var loadTex = AssetDatabase.LoadAssetAtPath<Texture>(path);
                    if (null == loadTex)
                        continue;

                    if (null != atlasSettings && !atlasSettings.CanBuildInAtlas(loadTex))
                        continue;

                    depenciesTexture.Enqueue(new(path, loadTex));
                }
            }

            PrepareMoveAssets(depenciesTexture.Select(v => v.Key).ToArray(), atlasSettings, null);
        }

        [MenuItem("Assets/shaco/SpriteAtlas/CollectDepencies", true)]
        static bool CollectDepenciesInPrefabValid_Asset()
        {
            return Selection.gameObjects.Length > 0 && Selection.gameObjects.Any(v => !string.IsNullOrEmpty(AssetDatabase.GetAssetPath(v)));
        }

        [MenuItem("GameObject/shaco/SpriteAtlas/CollectDepencies (Only Active Target)", false)]
        static void CollectDepenciesInPrefab_GameObject()
        {
            var atlasSettings = SpriteAtlasSettingsWindow.GetLastSelectAtlasSettings();
            var foundImages = Selection.activeGameObject.GetComponentsInChildren<UnityEngine.UI.Image>().Where(v => v.enabled && v.gameObject.activeInHierarchy);
            var checkDuplicateSprite = new Dictionary<Sprite, UnityEngine.UI.Image>();
            foreach (var image in foundImages)
            {
                if (null == image.sprite)
                    continue;

                if (null != atlasSettings && !atlasSettings.CanBuildInAtlas(image.sprite.texture))
                    continue;

                if (checkDuplicateSprite.ContainsKey(image.sprite))
                    continue;
                checkDuplicateSprite.Add(image.sprite, image);
            }

            PrepareMoveAssets(checkDuplicateSprite.Select(v => AssetDatabase.GetAssetPath(v.Key)).ToArray(), atlasSettings, result =>
            {
                if (1 != result)
                    Selection.objects = foundImages.Select(v => v.gameObject).ToArray();
            });
        }

        [MenuItem("GameObject/shaco/SpriteAtlas/CollectDepencies (Only Active Target)", true)]
        static bool CollectDepenciesInPrefabValid_GameObject()
        {
            if (null == Selection.activeGameObject)
                return false;

            if (Selection.gameObjects.Length > 1)
                return false;

            var v = Selection.activeGameObject;
            var foundImages = v.GetComponentsInChildren<UnityEngine.UI.Image>();
            if (foundImages.Any(v => v.enabled && v.gameObject.activeInHierarchy))
                return true;
            return false;
        }

        [MenuItem("Assets/shaco/SpriteAtlas/BackupAssetPath", false)]
        static void BackupAssetPath()
        {
            var textureGroup = new Dictionary<string, Queue<Texture>>();
            foreach (var iter in Selection.objects)
            {
                var assetPath = AssetDatabase.GetAssetPath(iter);
                var textureTmp = AssetDatabase.LoadAssetAtPath<Texture>(assetPath);
                if (null == textureTmp)
                    continue;

                var folderPath = System.IO.Path.GetDirectoryName(assetPath);
                var backupLogPath = $"{folderPath}/tmp_move_backup.log";
                if (!System.IO.File.Exists(backupLogPath))
                    continue;

                if (!textureGroup.TryGetValue(backupLogPath, out var findValue))
                    textureGroup.Add(backupLogPath, findValue = new());
                findValue.Enqueue(textureTmp);
            }

            foreach (var iter in textureGroup)
            {
                var folderPath = System.IO.Path.GetDirectoryName(iter.Key);
                var logData = ReadBackupLog(folderPath);

                foreach (var iter2 in iter.Value)
                {
                    var assetPath = AssetDatabase.GetAssetPath(iter2);
                    if (!logData.TryGetValue(assetPath, out var findValue))
                    {
                        Debug.LogError("没有在回滚文件中找到路径=" + assetPath);
                        continue;
                    }
                    AssetDatabase.MoveAsset(assetPath, findValue.from);
                    logData.Remove(assetPath);
                }

                WriteBackupLog(folderPath, logData.Values);
            }
        }

        [MenuItem("Assets/shaco/SpriteAtlas/BackupAssetPath", true)]
        static bool BackupAssetPathValid()
        {
            return Selection.objects.Any(v =>
            {
                var assetPath = AssetDatabase.GetAssetPath(v);
                var folderPath = System.IO.Path.GetDirectoryName(assetPath);
                var backupLogPath = $"{folderPath}/tmp_move_backup.log";
                if (System.IO.File.Exists(backupLogPath))
                    return true;

                return false;
            });
        }

        static void PrepareMoveAssets(string[] selectAssets, ISpriteAtlasSettings atlasSettings, System.Action<int> callbackCheck)
        {
            Debug.Log($"散图筛选规则(<color=green>{atlasSettings.ToTypeString()}</color>)，详情查看shaco/Tools/SpriteAtlasSettings (F11)");
            if (selectAssets.Length == 0)
            {
                Debug.Log("规则内没有需要筛选的散图");
                return;
            }

            shaco.Instance.FindReference.FindReferenceWindow.FindReferencesInProject(depencies =>
            {

                var sortDepencies = depencies.Where(v => !atlasSettings.CanBuildInGlobalAtlas(AssetDatabase.LoadAssetAtPath<Texture>(v.Key), v.Value.dependencies)).ToList();
                sortDepencies.Sort((v1, v2) => v2.Value.dependencies.Count.CompareTo(v1.Value.dependencies.Count));
                var result = EditorUtility.DisplayDialogComplex("确认", $"请选择对{sortDepencies.Count}张散图的操作", "移动", "取消", "仅日志");

                if (null != callbackCheck)
                    callbackCheck(result);

                if (2 == result)
                {
                    foreach (var iter in sortDepencies)
                    {
                        Debug.Log("被引用次数=" + iter.Value.dependencies.Count + " 路径=" + iter.Key, AssetDatabase.LoadAssetAtPath<Object>(iter.Key));
                    }
                }
                else if (0 == result)
                {
                    MoveAssetsProcess(sortDepencies);
                }
            }, true, selectAssets);
        }

        static void MoveAssetsProcess(List<KeyValuePair<string, shaco.Instance.FindReference.FindReferenceWindow.DependentInfo>> depencies)
        {
            var internalAtlasSettingsPath = shaco.GameHelper.gameConfig.ReadString(shaco.SpriteAtlasSettings.SETTINGS_PATH_KEY);
            var defaultPath = System.IO.File.Exists(internalAtlasSettingsPath) ? System.IO.Path.GetDirectoryName(internalAtlasSettingsPath) : Application.dataPath;
            var selectPath = EditorUtility.SaveFolderPanel($"请选择散图输出目录", defaultPath, "");
            if (!string.IsNullOrEmpty(selectPath))
            {
                if (!selectPath.Contains(Application.dataPath))
                {
                    Debug.LogError("输出目录必须在Unity/Assets目录下");
                    return;
                }
            }

            selectPath = EditorHelper.FullPathToUnityAssetPath(selectPath);
            var backupData = new Dictionary<string, BackupData>();
            foreach (var iter in depencies)
            {
                var moveFrom = iter.Key;
                var fileName = System.IO.Path.GetFileName(moveFrom);
                var moveTo = $"{selectPath}/{fileName}";
                AssetDatabase.MoveAsset(moveFrom, moveTo);

                //记录一份回滚目录，方便回滚一些不希望加入图集的资源
                if (backupData.ContainsKey(moveFrom))
                    Debug.LogError("移动资源路径重复, move from=" + moveFrom);
                else
                {
                    backupData.Add(moveFrom, new ()
                    {
                        from = moveFrom,
                        to = moveTo,
                        referenceCount = iter.Value.dependencies.Count
                    });
                }
            }

            //从本地文件读取
            var logData = ReadBackupLog(selectPath);
            foreach (var iter in logData)
            {
                backupData[iter.Key] = iter.Value;
            }

            WriteBackupLog(selectPath, backupData.Values);
        }

        struct BackupData
        {
            public string from;
            public string to;
            public int referenceCount;
        }
        static void WriteBackupLog(string folderPath, IEnumerable<BackupData> data)
        {
            var backupString = new System.Text.StringBuilder();
            foreach (var iter in data)
            {
                backupString.Append($"ref:{iter.referenceCount}##{iter.from}##{iter.to}\n");
            }

            if (backupString.Length > 0)
                backupString.Remove(backupString.Length - 1, 1);

            var savePath = $"{folderPath}/tmp_move_backup.log";
            System.IO.File.WriteAllText(savePath, backupString.ToString());
            Debug.Log("backup log save to=" + savePath);
        }

        static Dictionary<string, BackupData> ReadBackupLog(string folderPath)
        {
            var retValue = new Dictionary<string, BackupData>();
            var logPath = $"{folderPath}/tmp_move_backup.log";
    
            var backupLogPath = logPath;
            if (!System.IO.File.Exists(backupLogPath))
                return retValue;

            var readLogString = System.IO.File.ReadAllText(logPath).Replace("\r\n", "\n");
            var readLogStringSplit = readLogString.Split('\n');
            foreach (var iter2 in readLogStringSplit)
            {
                var splitTmp = iter2.Split("##");
                retValue.Add(splitTmp[2], new()
                {
                    from = splitTmp[1],
                    to = splitTmp[2],
                    referenceCount = int.Parse(splitTmp[0].Substring("ref:", ""))
                });
            }

            return retValue;
        }
    }
}